#!/usr/bin/env python
# -*- coding: utf-8 -*-

import itertools
import random
import os
import codecs
import shutil
import json
from norvig_sudoku import random_puzzle, solve
from assistivedialog import assistiveDialog
import sys
sys.path.append("../")
import scriptgoodies


def completedInstallation(env):
	path = os.path.join(env["prefs"]["pictoDirectory"],env["meta"]["name"])
	indexPath = os.path.join(path,env["constants"]["pictoIndexFileName"] % env["constants"]["language"])
	if os.path.isfile(indexPath):
		return True
	if not os.path.isdir(path):
		os.mkdir(path)
	images = []
	for digit in range(1,10):
		filename = "%s.png" % digit
		images.append([filename])
		shutil.copyfile(os.path.join("pictos",filename), os.path.join(path,filename))
		for couple in itertools.product("abcdefghi",repeat=2):
			images[-1].append({"1":"cwsudoku%s%s%s" % (couple[0],couple[1],chr(96+digit))})
	images.append([""])
	for couple in itertools.product("abcdefghi",repeat=2):
		images[-1].append({"1":"cwsudoku%s%s" % couple,"occ":1})
	shutil.copyfile(os.path.join("pictos","solution.png"), os.path.join(path,"solution.png"))
	images.append(["solution.png",{"1":"cwsudoku%s" % env["static"]["cwSudoku_solutionSuffix"].lower(),"occ":1}])
	pictoIndex = {
		"version": "1.1",
		"format": "2.0",
		"set": "cwSudoku",
		"author": "Aristide Grange",
		"url": "http://chewingword.wikidot.com/",
		"mail": "chewingword@wingi.net",
		"license": "CeCILL 2.0",
		"images": images
	}
	codecs.open(indexPath,"w","utf8").write(json.dumps(pictoIndex))

	
def main(env):

	class Grid(list):
		def __init__(self,level):
			try:
				grids = dict(enumerate(open("%s.txt" % level).read().split("\n")))
				knownGridsPath = "../../scripts (user)/cwSudoku_knownGrids.json"
				try:
					knownGrids = json.loads(codecs.open(knownGridsPath,"r","utf8").read())
					knownGrids[level] = knownGrids.get(level,[])
				except:
					knownGrids = {level:[]}
				if len(grids) > len(knownGrids[level]):
					for knownGrid in knownGrids[level]:
						grids.pop(knownGrid,None) # raises no exception
				else:
					assistiveDialog.warning(env["static"]["cwSudoku_exhaustedMessage"] % level)
					knownGrids[level] = []
				k = random.choice(grids.keys())
				self.initial = grids[k]
				knownGrids[level].append(k)
				codecs.open(knownGridsPath,"w","utf8").write(json.dumps(knownGrids))
			except IOError:
				while True:
					self.initial = random_puzzle()
					if solve(self.initial):
						break
			list.__init__(self,self.initial[:])
			self.shots = 0
			self.indexToPosition = ["%s%s" % couple for couple in itertools.product("abcdefghi",repeat=2)]
			self.peers = {}
			self.mutablePeers = {}
			self.immutablePeers = {}
			for (index,digit) in enumerate(self):
				if digit == ".":
					leftIndex = (index / 9) * 9
					topIndex = index % 9
					cornerIndex = (leftIndex / 27) * 27 + (topIndex / 3) * 3
					self.peers[index] = set(range(leftIndex,leftIndex+9) + range(topIndex,topIndex+81,9) + range(cornerIndex,cornerIndex+3) + range(cornerIndex+9,cornerIndex+12) + range(cornerIndex+18,cornerIndex+21)) - set([index])
					self.mutablePeers[index] = sorted(peer for peer in self.peers[index] if peer>index and self[peer]==".")
					self.immutablePeers[index] = [peer for peer in self.peers[index] if self[peer]!="."]
			
		def __setitem__(self,index,value):
			list.__setitem__(self,index,value)
			self.shots += 1
		
		def hasImmutableConflict(self,index,digit):
			for peer in self.immutablePeers[index]:
				if digit == self[peer]:
					return True
		
		def __str__(self):
			result = ["\n"]
			for (i,position,initialChar,currentChar) in itertools.izip(range(81),self.indexToPosition,self.initial,self):
				if 0 < i < 80 and i % 3 == 0:
					if i % 9 == 0:
						result.append("\n")
						if i % 27 == 0:
							result.append('+'.join(['-'*15]*3) + "\n")
					else:
							result.append("|")
				if initialChar == ".":
					result.append(" %s%s " % (position,currentChar))
				else:
					result.append("   %s " % initialChar)
			return "".join(result)
		
		def getMutableConflicts(self):
			result = []
			for (index,digit) in enumerate(self):
				if digit != ".":
					for peer in self.mutablePeers.get(index,[]):
						if digit == self[peer]:
							result.append({"digit":digit,"index":self.indexToPosition[index],"peer":self.indexToPosition[peer]})
			return "".join(env["static"]["cwSudoku_conflictItem"] % item for item in result)
		
		def getDirectives(self,suffix="",message=""):
			text = "(=%s%s%s\n)" % (message,self,self.getMutableConflicts())
			return [("replace",env["meta"]["name"]+suffix+text),("moveCursor",-len(text))]
		
	
	if not completedInstallation(env):
		return [("messageBox",env["static"]["cwSudoku_completedInstallationMessage"])]
	if env["context"]["selection"] == "":
		if "cwSudoku_grid" not in env["private"]:
			env["private"]["cwSudoku_grid"] = Grid(env["prefs"]["cwSudoku_level"])
		scriptgoodies.setTemporaryPrefs(env,{
			"editorFontFamily": env["constants"]["outputDialogFontFamily"],
			"shortTermAdaptationDeleteOnQuit": False, # hidden preference. Do not save.
			"keyboardOrder": "alphabetic",
			"pictoMode": True,
			"toothDirectPictoMode": True,
			"toothIndirectPictoMode": False,
			"farDirectPictoMode": False,
			"farIndirectPictoMode": False,
			"progressiveMode": False,
			"resizeCharMode": False,
			"scannerName": "sisyphus",
			"scanJump": 4,
		})
		return env["private"]["cwSudoku_grid"].getDirectives() + [("updatePrefs",None)]
	if env["meta"]["poorSuffix"] == "" or "cwSudoku_grid" not in env["private"]:
		return [("replace",""),("updatePrefs",scriptgoodies.restoreBackupPrefs(env))]
	grid = env["private"]["cwSudoku_grid"]
	if env["meta"]["poorSuffix"] == env["static"]["cwSudoku_solutionSuffix"]:
		lastGrid = str(grid)
		grid[:] = [digit for (_,digit) in sorted(solve(grid.initial).iteritems())]
		del env["private"]["cwSudoku_grid"]
		return grid.getDirectives(message = lastGrid + env["static"]["cwSudoku_solutionMessage"])
	for (index,position) in enumerate(grid.indexToPosition):
		if env["meta"]["poorSuffix"][:2] == position.upper():
			if grid.initial[index] != ".":
				return grid.getDirectives(message = env["static"]["cwSudoku_immutablePositionMessage"] % position)
			if env["meta"]["poorSuffix"][2:] == "":
				possibleDigits = sorted(list(set("123456789") - set(grid[peer] for peer in grid.peers[index])))
				grid[index] = (possibleDigits[0] if len(possibleDigits)==1 and grid[index]=="." else ".")
				return grid.getDirectives(message = env["static"]["cwSudoku_possibleDigitsMessage"] % (grid.indexToPosition[index],(" ".join(possibleDigits) if possibleDigits else env["static"]["cwSudoku_noPossibleDigitsString"])))
			if env["meta"]["poorSuffix"][2:] in list("ABCDEFGHI"):
				digit = str(ord(env["meta"]["poorSuffix"][2:])-64)
				if grid.hasImmutableConflict(index,digit):
					return grid.getDirectives(message = env["static"]["cwSudoku_rejectedDigitMessage"])
				grid[index] = digit
				if "." in grid or grid.getMutableConflicts():
					return grid.getDirectives()
				del env["private"]["cwSudoku_grid"]
				return grid.getDirectives(message = env["static"]["cwSudoku_successMessage"] % {"shots":grid.shots,"dots":grid.initial.count(".")})
			return grid.getDirectives(suffix = position, message = env["static"]["cwSudoku_invalidDigitMessage"] % env["meta"]["poorSuffix"][2:])
	return grid.getDirectives(message = env["static"]["cwSudoku_invalidPositionMessage"] % position)

